package com.xinqi.modules.exam.paper.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.google.common.collect.ImmutableList;
import com.xinqi.common.base.dto.utils.QuestionStats;
import com.xinqi.common.base.dto.utils.QuestionUtils;
import com.xinqi.common.base.enums.YesNoEnum;
import com.xinqi.common.base.response.PageInfo;
import com.xinqi.common.base.response.Param;
import com.xinqi.common.base.utils.lambda.Lambdas;
import com.xinqi.common.base.utils.spring.Spring;
import com.xinqi.common.mybatis.utils.Pages;
import com.xinqi.common.mybatis.utils.Queries;
import com.xinqi.common.security.utils.UserHelp;
import com.xinqi.modules.exam.common.assistant.ChatAssistantExamService;
import com.xinqi.modules.exam.common.assistant.enums.MessageSubTypeEnum;
import com.xinqi.modules.exam.common.constants.ExamI18nConstants;
import com.xinqi.modules.exam.common.delayed.ExamDelayQueueService;
import com.xinqi.modules.exam.common.delayed.ExamTopicEventService;
import com.xinqi.modules.exam.common.enums.ExamLimitEnum;
import com.xinqi.modules.exam.common.enums.ExamQueueEnum;
import com.xinqi.modules.exam.common.error.ExamErrors;
import com.xinqi.modules.exam.common.manager.CommonClassManager;
import com.xinqi.modules.exam.common.manager.CourseContentManager;
import com.xinqi.modules.exam.paper.convert.CourseExamPaperConfigConvert;
import com.xinqi.modules.exam.paper.convert.CourseExamPaperConvert;
import com.xinqi.modules.exam.paper.domain.CourseExamPaperConfigEntity;
import com.xinqi.modules.exam.paper.domain.CourseExamPaperEntity;
import com.xinqi.modules.exam.paper.domain.CourseExamPaperStatsEntity;
import com.xinqi.modules.exam.paper.dto.req.CourseExamPaperCreateDTO;
import com.xinqi.modules.exam.paper.dto.req.CourseExamPaperDeleteDTO;
import com.xinqi.modules.exam.paper.dto.req.CourseExamPaperQueryDTO;
import com.xinqi.modules.exam.paper.dto.req.CourseExamPaperReleaseDTO;
import com.xinqi.modules.exam.paper.dto.req.CourseExamPaperUpdateDTO;
import com.xinqi.modules.exam.paper.dto.rsp.CourseExamPaperDetailDTO;
import com.xinqi.modules.exam.paper.dto.rsp.CourseExamPaperResultDTO;
import com.xinqi.modules.exam.paper.dto.rsp.CourseExamPaperStatusNumDTO;
import com.xinqi.modules.exam.paper.dto.rsp.CourseExamPaperStudentDTO;
import com.xinqi.modules.exam.paper.enums.ExamStatusEnum;
import com.xinqi.modules.exam.paper.manager.CourseExamPaperConfigManager;
import com.xinqi.modules.exam.paper.manager.CourseExamPaperManager;
import com.xinqi.modules.exam.paper.manager.CourseExamPaperStatsManager;
import com.xinqi.modules.exam.paper.mapper.CourseExamPaperConfigMapper;
import com.xinqi.modules.exam.paper.mapper.CourseExamPaperMapper;
import com.xinqi.modules.exam.paper.service.CourseExamPaperService;
import com.xinqi.modules.exam.student.manager.CourseExamPaperStudentManager;
import com.xinqi.modules.exam.student.service.CourseExamPaperStudentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.xinqi.common.base.error.CommonErrors.BAD_REQUEST;

/**
 * 课程测评服务层实现
 *
 * @author: linkai
 * @date: 2023/07/02
 */
@Slf4j
@Validated
@Service("courseExamPaperService")
@RequiredArgsConstructor
public class CourseExamPaperServiceImpl implements CourseExamPaperService {
    private final CourseExamPaperConvert courseExamPaperConvert;
    private final CourseExamPaperManager courseExamPaperManager;
    private final ChatAssistantExamService chatAssistantExamService;
    private final CourseExamPaperMapper courseExamPaperMapper;
    private final CourseExamPaperConfigMapper courseExamPaperConfigMapper;
    @Lazy
    private final ExamDelayQueueService examDelayQueueService;
    @Lazy
    private final CourseExamPaperStudentService courseExamPaperStudentService;
    private final ExamTopicEventService examTopicEventService;
    private final CourseExamPaperConfigManager courseExamPaperConfigManager;
    private final CourseExamPaperStatsManager courseExamPaperStatsManager;
    private final CourseExamPaperStudentManager courseExamPaperStudentManager;
    private final CourseExamPaperConfigConvert courseExamPaperConfigConvert;
    private final IdentifierGenerator defaultIdentifierGenerator;
    private final CourseContentManager courseContentManager;
    private final CommonClassManager commonClassManager;

    /**
     * 分页查询 课程测评数据
     */
    @Override
    @DS("slaver")
    public PageInfo<CourseExamPaperResultDTO> page(Param pageable, CourseExamPaperQueryDTO query) {
        IPage<CourseExamPaperResultDTO> page = courseExamPaperManager.findPage(
            Pages.page(pageable, OrderItem.desc("a.id")),
            query
        );
        return Pages.convert(page);
    }

    /**
     * 查询 课程测评数据
     */
    @Override
    public List<CourseExamPaperResultDTO> find(CourseExamPaperQueryDTO query) {
        return courseExamPaperManager.list(wrapper(query)).stream()
            .map(courseExamPaperConvert::convert).collect(Collectors.toList());
    }

    /**
     * 查询 课程测评数据
     */
    @Override
    public List<CourseExamPaperResultDTO> findList(CourseExamPaperQueryDTO dto) {
        return courseExamPaperManager.findList(dto);
    }

    /**
     * 查询 课程测评数据 -- 学生端
     */
    @Override
    public List<CourseExamPaperStudentDTO> findStudentList(CourseExamPaperQueryDTO dto) {
        return courseExamPaperManager.findStudentList(dto);
    }

    /**
     * 查询 课程测评数据 -- 学生端
     */
    @Override
    @DS("slaver")
    public PageInfo<CourseExamPaperStudentDTO> pageStudentList(Param pageable, CourseExamPaperQueryDTO dto) {
        IPage<CourseExamPaperStudentDTO> page = courseExamPaperManager.pageStudentList(
            Pages.page(pageable, OrderItem.desc(Pages.ID)),
            dto
        );
        return Pages.convert(page);
    }

    private LambdaQueryWrapper<CourseExamPaperEntity> wrapper(CourseExamPaperQueryDTO query) {
        LambdaQueryWrapper<CourseExamPaperEntity> wrapper = Queries.lambda();
        Queries.accept(query.getId(), v -> wrapper.eq(CourseExamPaperEntity::getId, v));
        return wrapper;
    }

    /**
     * 根据id查询 课程测评数据
     */
    @Override
    public CourseExamPaperResultDTO findById(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return Optional.ofNullable(courseExamPaperManager.findById(id)).map(courseExamPaperConvert::convert)
            .orElseThrow(() -> ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在));
    }

    @Override
    public CourseExamPaperDetailDTO findDetailById(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), "ID不能为空");
        return Optional.ofNullable(courseExamPaperManager.findById(id)).map(courseExamPaperConvert::detail)
            .orElseThrow(() -> ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在));
    }

    /**
     * 新增 课程测评数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insert(@Valid CourseExamPaperCreateDTO dto) {
        CourseExamPaperEntity entity = courseExamPaperConvert.create(dto);
        entity.setUserId(UserHelp.userId());
        entity.setStatus(ExamStatusEnum.草稿);
        entity.setIsRelease(YesNoEnum.NO.ordinal());
        // 初始化题目列表
        questionInit(entity);
        // 初始化题目汇总信息
        initQuestionStats(entity);
        // 保存当前数据
        courseExamPaperManager.save(entity);
        // 初始化配置
        courseExamPaperConfigManager.initConfig(entity);
        // 初始化统计数据
        courseExamPaperStatsManager.initConfig(entity);
        // 同步课程内容
        courseContentManager.syncCourseContent(courseExamPaperManager.findById(entity.getId()));
        return entity.getId();
    }

    private void questionInit(CourseExamPaperEntity entity) {
        if (entity.getQuestionContent() != null) {
            BAD_REQUEST.check(ExamLimitEnum.试卷题目上限.validate(entity.getQuestionContent().size()), "题目数量超过上限");
            // 生成试题id
            entity.getQuestionContent().forEach(v -> v.setId(defaultIdentifierGenerator.nextId(null).longValue()));
        } else {
            entity.setQuestionContent(new ArrayList<>());
        }
    }

    private void initQuestionStats(CourseExamPaperEntity entity) {
        QuestionStats status = QuestionUtils.status(entity.getQuestionContent());
        entity.setTotalScore(status.getTotalScore());
        entity.setQuestionTotal(status.getQuestionTotal());
        entity.setQuestionTypeTotal(status.getQuestionTypeTotal());
        entity.setSubjectiveQuestionNum(status.getSubjectiveQuestionNum());
        entity.setObjectiveQuestionNum(status.getObjectiveQuestionNum());
    }

    /**
     * 根据id修改 课程测评数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateById(@Valid CourseExamPaperUpdateDTO dto) {
        CourseExamPaperEntity old = Optional.ofNullable(courseExamPaperManager.getById(dto.getId()))
            .orElseThrow(() -> ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在));

        // 只有在 草稿、 定时发布 状态才能修改
        BAD_REQUEST.check(Objects.equals(old.getStatus(), ExamStatusEnum.草稿)
                || Objects.equals(old.getStatus(), ExamStatusEnum.定时发布),
            "测评状态错误");
        if (Objects.isNull(dto.getChapterId())) {
            dto.setChapterId(0L);
        }
        CourseExamPaperEntity entity = courseExamPaperConvert.update(dto);
        // 初始化题目列表
        questionInit(entity);
        // 初始化题目汇总信息
        initQuestionStats(entity);
        // 需要带状态更新，防止并发问题
        entity.setStatus(old.getStatus());
        // 更新测评数据
        BAD_REQUEST.check(courseExamPaperManager.updateByIdAndStatus(entity), "测评更新失败");
        // 同步课程内容
        courseContentManager.syncCourseContent(entity);
        return true;
    }

    /**
     * 根据id删除 课程测评数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(Long id) {
        BAD_REQUEST.check(Objects.nonNull(id), ExamI18nConstants.ID不能为空);
        return deleteByIds(ImmutableList.of(id));
    }

    /**
     * 根据id删除 课程测评数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteById(CourseExamPaperDeleteDTO dto) {
        return deleteByIds(ImmutableList.of(dto.getId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return false;
        }
        List<Long> list = new ArrayList<>(ids.size());
        List<CourseExamPaperEntity> startList = new ArrayList<>(ids.size());
        for (Long id : ids) {
            CourseExamPaperEntity entity = courseExamPaperManager.findById(id);
            if (entity == null) {
                throw ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在);
            }
            BAD_REQUEST.check(Objects.equals(entity.getStatus(), ExamStatusEnum.草稿)
                || Objects.equals(entity.getStatus(), ExamStatusEnum.已结束)
                || Objects.equals(entity.getStatus(), ExamStatusEnum.已发布)
                || Objects.equals(entity.getStatus(), ExamStatusEnum.已开始), "测评状态错误");
            list.add(id);
            if (Objects.equals(entity.getStatus(), ExamStatusEnum.已开始)) {
                startList.add(entity);
            }
        }
        boolean b = courseExamPaperManager.deleteByIds(list);
        if (b) {
            // 删除 课堂内容
            courseContentManager.deleteByExamIds(list);
            if (CollectionUtils.isNotEmpty(startList)) {
                // 发送消息
                Spring.committedAsync(() ->
                    startList.forEach(v -> {
                        List<Long> studentIdByNotPaper = courseExamPaperStudentManager.findStudentIdByExamId(v.getId());
                        chatAssistantExamService.sendRYunMessage(v, studentIdByNotPaper, MessageSubTypeEnum.测评已被删除);
                    })
                );
            }
        }
        return b;
    }

    /**
     * 发布测评
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean release(CourseExamPaperReleaseDTO dto) {
        Long examId = dto.getExamPaperId();
        BAD_REQUEST.check(Objects.nonNull(examId), "测评ID不能为空");
        CourseExamPaperEntity examPaper = Optional.ofNullable(courseExamPaperManager.findById(examId))
            .orElseThrow(() -> ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在));

        CourseExamPaperConfigEntity config = Optional.ofNullable(courseExamPaperConfigManager.findByExamId(examId))
            .orElseThrow(() -> BAD_REQUEST.asException("测评配置不存在"));
        LocalDateTime now = LocalDateTime.now();
        // 校验数据
        checkData(now, dto, examPaper, config);
        // 交卷查看测评
        config.setIsSubmitView(dto.getIsSubmitView());
        // 成绩公布
        config.setIsPublishScore(dto.getIsPublishScore());
        // 答案公布
        config.setIsPublishAnswer(dto.getIsPublishAnswer());
        // 是否同步班级
        boolean isSyncClass = false;
        if (Objects.equals(config.getIsSend(), YesNoEnum.NO.ordinal())
            && Objects.equals(config.getIsSendClass(), YesNoEnum.YES.ordinal())
        ) {
            config.setIsSend(YesNoEnum.YES.ordinal());
            isSyncClass = true;
        }
        // 更新数据
        courseExamPaperManager.updateById(examPaper);
        courseExamPaperConfigManager.updateById(config);
        courseContentManager.syncCourseContent(examPaper);
        // 同步班级数据
        if (isSyncClass) {
            // 防止缓存问题
            syncClass(
                courseExamPaperMapper.selectById(examId),
                courseExamPaperConfigMapper.selectById(config.getId())
            );
        }
        return true;
    }

    public void syncClass(CourseExamPaperEntity oldExamPaper, CourseExamPaperConfigEntity OldConfig) {
        List<Long> classIdList = OldConfig.getSendClass();
        // 需要同步的班级为空
        if (CollectionUtils.isEmpty(classIdList)) {
            return;
        }
        List<CourseExamPaperEntity> examPaperList = new ArrayList<>(classIdList.size());
        List<CourseExamPaperConfigEntity> examConfigList = new ArrayList<>(classIdList.size());
        List<CourseExamPaperStatsEntity> examStatsList = new ArrayList<>(classIdList.size());
        // 生成新的测评数据
        for (Long classId : classIdList) {
            // 测评信息
            CourseExamPaperEntity exam = courseExamPaperConvert.copy(oldExamPaper);
            exam.setId(defaultIdentifierGenerator.nextId(null).longValue());
            exam.setChapterId(null);
            exam.setClassId(classId);
            examPaperList.add(exam);
            // 测评配置
            CourseExamPaperConfigEntity config = courseExamPaperConfigConvert.copy(OldConfig);
            config.setId(defaultIdentifierGenerator.nextId(null).longValue());
            config.setExamPaperId(exam.getId());
            config.setIsSend(YesNoEnum.NO.ordinal());
            config.setIsSendClass(YesNoEnum.NO.ordinal());
            config.setSendClass(null);
            examConfigList.add(config);
            // 测评统计
            CourseExamPaperStatsEntity examPaperStats = new CourseExamPaperStatsEntity();
            examPaperStats.setExamPaperId(exam.getId());
            examStatsList.add(examPaperStats);
        }
        // 保存测评数据
        courseExamPaperManager.saveBatch(examPaperList);
        courseExamPaperConfigManager.saveBatch(examConfigList);
        courseExamPaperStatsManager.saveBatch(examStatsList);
        // 提交事件
        examPaperList.forEach(examPaper -> {
            Long examId = examPaper.getId();
            // 定时发布
            if (Objects.equals(examPaper.getIsTimedRelease(), YesNoEnum.YES.ordinal())) {
                // 定时发布 - 延时任务
                Spring.committed(() -> examDelayQueueService.publishTask(ExamQueueEnum.发布测评, examId, examPaper.getReleaseTime()));
            }
            // 发送消息事件
            nowReleaseOther(examPaper);
            // 同步课程内容
            courseContentManager.syncCourseContent(examPaper);
        });
    }

    private void checkData(LocalDateTime now, CourseExamPaperReleaseDTO dto, CourseExamPaperEntity examPaper, CourseExamPaperConfigEntity config) {
        Long examId = examPaper.getId();
        ExamStatusEnum status = examPaper.getStatus();
        // 修改状态
        switch (status) {
            case 草稿:
                Lambdas.ofNull(dto.getEndTime(), "结束时间不能为空");
                courseExamPaperConvert.merge(dto, config);
                if (dto.getReleaseTime() == null) {
                    dto.setReleaseTime(now);
                }
                if (dto.getStartTime() == null) {
                    dto.setStartTime(now);
                }
                if (CollectionUtils.isEmpty(examPaper.getQuestionContent())) {
                    throw BAD_REQUEST.asException("该测评未添加题目");
                }
                Optional.ofNullable(dto.getExamPaperName()).ifPresent(examPaper::setName);
                Optional.ofNullable(dto.getExamPaperContent()).ifPresent(examPaper::setContent);
                examPaper.setChapterId(dto.getChapterId());
                examPaper.setReleaseTime(dto.getReleaseTime());
                examPaper.setStartTime(dto.getStartTime());
                examPaper.setEndTime(dto.getEndTime());
                // 是否为定时发布
                if (Objects.equals(examPaper.getIsTimedRelease(), YesNoEnum.YES.ordinal())) {
                    // 校验时间
                    checkExamTime(now, dto.getReleaseTime(), examPaper.getStartTime(), examPaper.getEndTime());
                    // 定时发布配置
                    timedRelease(examPaper, dto);
                } else {
                    // 校验时间
                    checkExamTime(now, examPaper.getStartTime(), examPaper.getEndTime());
                    // 立即发布配置
                    nowRelease(now, examPaper);
                }
                break;
            case 定时发布:
                // 从 定时发布 修改为 立即发布
                if (Objects.equals(examPaper.getIsTimedRelease(), YesNoEnum.YES.ordinal())
                    && Objects.equals(dto.getIsTimedRelease(), YesNoEnum.NO.ordinal())) {
                    // 移除定时发布时间 - 延时任务
                    Spring.committed(() -> examDelayQueueService.removerTask(ExamQueueEnum.发布测评, examId));
                    // 立即发布配置
                    nowRelease(now, examPaper);
                } else if (!Objects.equals(examPaper.getReleaseTime(), dto.getReleaseTime())) {
                    // 更新定时发布时间 - 延时任务
                    Spring.committed(() -> examDelayQueueService.removerTask(ExamQueueEnum.发布测评, examId));
                    // 延时发布配置
                    timedRelease(examPaper, dto);
                }
                break;
            case 已发布:
                if (dto.getStartTime() == null) {
                    dto.setStartTime(now);
                }
                if (dto.getIsLimitAnswerTime() != null) {
                    config.setIsLimitAnswerTime(dto.getIsLimitAnswerTime());
                    config.setAnswerDuration(dto.getAnswerDuration());
                }
                examPaper.setChapterId(dto.getChapterId());
                examPaper.setStartTime(dto.getStartTime());
                if (dto.getEndTime() != null) {
                    examPaper.setEndTime(dto.getEndTime());
                }
                if (Objects.equals(now, dto.getStartTime())) {
                    examPaper.setStatus(ExamStatusEnum.已开始);
                    // 生成学生试卷 - 事件
                    Spring.committed(() -> {
                        // 结束测评 - 任务
                        examDelayQueueService.removerTask(ExamQueueEnum.开始测评, examId);
                        // 减去 1秒 防止 前端延迟
                        LocalDateTime endTime = examPaper.getEndTime().plusSeconds(-1);
                        examDelayQueueService.publishTask(ExamQueueEnum.结束测评, examId, endTime);
                    });
                } else {
                    // 减去 1秒 防止 前端延迟
                    LocalDateTime startTime = dto.getStartTime().plusSeconds(-1);
                    // 更新开始测评 - 延时任务
                    Spring.committed(() -> examDelayQueueService.updateTask(ExamQueueEnum.开始测评, examId, startTime));
                }
                break;
            case 已开始:
                // 07-29调整： 测试开始以后，不能修改结束时间
//                if (!Objects.equals(examPaper.getEndTime(), dto.getEndTime())) {
//                    // 更新结束测评 - 延时任务
//                    Spring.committed(() -> examDelayQueueService.updateTask(ExamQueueEnum.结束测评, examId, dto.getEndTime()));
//                }
//                examPaper.setEndTime(dto.getEndTime());
                break;
        }
        // 开始时间加上限时时长要小于结束时间
        if (!Objects.equals(status, ExamStatusEnum.已结束) && Objects.equals(config.getIsLimitAnswerTime(), YesNoEnum.YES.ordinal())) {
            BAD_REQUEST.check(
                examPaper.getStartTime().plusMinutes(config.getAnswerDuration())
                    .isBefore(examPaper.getEndTime()),
                "开始时间加上限时时长要小于结束时间");
        }
    }

    private void checkExamTime(LocalDateTime now, LocalDateTime releaseTime, LocalDateTime startTime, LocalDateTime endTime) {
        BAD_REQUEST.check(releaseTime.compareTo(now) >= 0, "定时发布时间不能小于当前时间");
        BAD_REQUEST.check(startTime.isAfter(releaseTime), "开始时间需大于定时发布时间");
        checkExamTime(now, startTime, endTime);
    }

    private void checkExamTime(LocalDateTime now, LocalDateTime startTime, LocalDateTime endTime) {
        BAD_REQUEST.check(startTime.compareTo(now) >= 0, "开始时间不能小于当前时间");
        BAD_REQUEST.check(endTime.isAfter(startTime), "截止时间需大于开始时间");
    }

    /**
     * 立即发布配置
     */
    private void nowRelease(LocalDateTime now, CourseExamPaperEntity examPaper) {
        examPaper.setStatus(ExamStatusEnum.已发布);
        examPaper.setIsRelease(YesNoEnum.YES.ordinal());
        examPaper.setIsTimedRelease(YesNoEnum.NO.ordinal());
        examPaper.setReleaseTime(now);
        if (Objects.equals(now, examPaper.getStartTime())) {
            examPaper.setStatus(ExamStatusEnum.已开始);
        }
        // 发送消息事件
        nowReleaseOther(examPaper);
    }

    private void nowReleaseOther(CourseExamPaperEntity examPaper) {
        Long examId = examPaper.getId();
        // 是否开始
        if (Objects.equals(examPaper.getStatus(), ExamStatusEnum.已开始)) {
            // 生成学生试卷 - 事件
            Spring.committed(() -> {
                // 生产试卷
                examTopicEventService.publishEvent(ExamQueueEnum.生成试卷, examId);
                // 结束测评 - 任务
                examDelayQueueService.publishTask(ExamQueueEnum.结束测评, examId, examPaper.getEndTime());
            });
            // 发送开始消息
            Spring.committedAsync(() -> {
                List<Long> userIds = commonClassManager.findRelationShipAndTeacherIdByClassId(examPaper.getClassId());
                chatAssistantExamService.sendMessage(examId, userIds, MessageSubTypeEnum.开始);
            });
        } else {
            // 生成学生试卷 - 事件
            Spring.committed(() -> {
                // 生产试卷
                examTopicEventService.publishEvent(ExamQueueEnum.生成试卷, examId);
                // 开始测评 - 延时任务
                examDelayQueueService.publishTask(ExamQueueEnum.开始测评, examId, examPaper.getStartTime());
            });
            // 发送发布消息
            Spring.committedAsync(() -> {
                List<Long> userIds = commonClassManager.findUserIdByClassId(examPaper.getClassId());
                chatAssistantExamService.sendMessage(examId, userIds, MessageSubTypeEnum.发布);
            });
        }
    }

    /**
     * 定时发布配置
     */
    private void timedRelease(CourseExamPaperEntity examPaper, CourseExamPaperReleaseDTO dto) {
        Long examId = examPaper.getId();
        examPaper.setStatus(ExamStatusEnum.定时发布);
        examPaper.setIsRelease(YesNoEnum.NO.ordinal());
        examPaper.setIsTimedRelease(YesNoEnum.YES.ordinal());
        examPaper.setReleaseTime(dto.getReleaseTime());
        // 定时发布 - 延时任务
        Spring.committed(() -> examDelayQueueService.publishTask(ExamQueueEnum.发布测评, examId, dto.getReleaseTime()));
    }

    /**
     * 处理 - 延时发布
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean handlerTimedRelease(Long examId) {
        CourseExamPaperEntity examPaper = Optional.ofNullable(courseExamPaperManager.findBaseById(examId))
            .orElseThrow(() -> ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在));
        BAD_REQUEST.check(Objects.equals(examPaper.getStatus(), ExamStatusEnum.定时发布),
            "测评状态必须为: " + ExamStatusEnum.定时发布.name());
        CourseExamPaperEntity temp = new CourseExamPaperEntity();
        temp.setId(examId);
        temp.setStatus(ExamStatusEnum.已发布);
        temp.setIsRelease(YesNoEnum.YES.ordinal());
        boolean flag = courseExamPaperManager.updateById(temp);
        // 生成学生试卷 - 事件
        Spring.committed(() -> examTopicEventService.publishEvent(ExamQueueEnum.生成试卷, examId));
        return flag;
    }

    /**
     * 处理- 开始测评
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlerStartExam(Long examId) {
        CourseExamPaperEntity examPaper = Optional.ofNullable(courseExamPaperManager.findBaseById(examId))
            .orElseThrow(() -> ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在));
        BAD_REQUEST.check(Objects.equals(examPaper.getStatus(), ExamStatusEnum.已发布),
            "测评状态必须为: " + ExamStatusEnum.已发布.name());
        courseExamPaperManager.updateStatusById(examId, ExamStatusEnum.已开始);
        // 添加结束测评 - 延时任务
        Spring.committed(() -> examDelayQueueService.publishTask(ExamQueueEnum.结束测评, examId, examPaper.getEndTime()));
        // 发送开始消息
        Spring.committedAsync(() -> {
            List<Long> userIds = commonClassManager.findRelationShipAndTeacherIdByClassId(examPaper.getClassId());
            chatAssistantExamService.sendMessage(examId, userIds, MessageSubTypeEnum.开始);
        });
    }

    /**
     * 处理- 结束测评延时系统批阅
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlerEndExam(Long examId) {
        handlerEndExam(examId, null);
    }

    /**
     * 处理- 结束测评
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlerEndExam(Long examId, LocalDateTime endTime) {
        CourseExamPaperEntity examPaper = Optional.ofNullable(courseExamPaperManager.findBaseById(examId))
            .orElseThrow(() -> ExamErrors.EXAM_NOT_EXIST.asException(ExamI18nConstants.测评不存在));
        BAD_REQUEST.check(Objects.equals(examPaper.getStatus(), ExamStatusEnum.已开始),
            "测评状态必须为: " + ExamStatusEnum.已开始.name());
        CourseExamPaperEntity temp = new CourseExamPaperEntity();
        temp.setId(examId);
        if (endTime != null) {
            temp.setEndTime(endTime);
        }
        temp.setStatus(ExamStatusEnum.已结束);
        courseExamPaperManager.updateById(temp);
        // 强制收卷
        courseExamPaperStudentService.forcePaper(examId);
        // 添加结束测评 任务，延迟1分钟系统统一批阅试卷
        Spring.committed(() -> {
            examDelayQueueService.removerTask(ExamQueueEnum.结束测评, examId);
            LocalDateTime _endTime = endTime != null ? endTime : examPaper.getEndTime();
            examDelayQueueService.publishTask(ExamQueueEnum.延时批阅, examId, _endTime.plusMinutes(1));
        });
//        Spring.committedAsync(() -> {
//            // 发送结束消息
//            List<Long> studentIds = courseExamPaperStudentManager.findStudentIdByExamId(examId);
//            chatAssistantExamService.sendMessage(examId, studentIds, MessageSubTypeEnum.结束);
//        });
    }

    /**
     * 手动结束测评
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean manualEnd(Long examId) {
        handlerEndExam(examId, LocalDateTime.now());
        log.info("=====> 手动结束测评！ id = {}", examId);
        return true;
    }

    @Override
    public CourseExamPaperStatusNumDTO findStatusNum(Long classId) {
        return courseExamPaperManager.findStatusNum(classId);
    }

    @Override
    public CourseExamPaperStatusNumDTO findStudentStatusNum(Long classId, Long studentId) {
        return courseExamPaperManager.findStudentStatusNum(classId, studentId);
    }

}
